﻿
using System.Reflection;
namespace zijian666.DI;
public static partial class ConfigureServicesExtensions
{
    /// <summary>
    /// 匹配参数
    /// </summary>
    /// <param name="argumentProvider">参数提供程序</param>
    /// <param name="parameters">参数描述</param>
    /// <param name="args">额外的参数</param>
    /// <returns></returns>
    private static object?[] GetArguments(this IServiceProvider argumentProvider, ParameterInfo[] parameters, params object[] args)
        => parameters?.Select(x => argumentProvider.GetArgument(x, args)).ToArray() ?? [];

    /// <summary>
    /// 根据 <paramref name="parameter"/> 在 <paramref name="argumentProvider"/> 和 <paramref name="args"/>中获取匹配的参数 <br /> 
    /// 如果未找到合适的参数则使用参数默认值 <br />
    /// 如果参数没有默认值,则使用参数类型对应的默认值 default(Type)
    /// </summary>
    /// <param name="argumentProvider">参数提供程序</param>
    /// <param name="parameter">参数描述</param>
    /// <param name="args">额外的参数</param>
    /// <returns></returns>
    private static object? GetArgument(this IServiceProvider argumentProvider, ParameterInfo parameter, params object[] args)
    {
        var value = GetArgument(argumentProvider, parameter.ParameterType, args);
        if (value is not null)
        {
            return value;
        }

        if (parameter.HasDefaultValue)
        {
            // 如果参数是 可空枚举类型 DefaultValue 会是一个 int
            if (parameter.DefaultValue is not null && Nullable.GetUnderlyingType(parameter.ParameterType) is Type enumType && enumType.IsEnum)
            {
                return Enum.ToObject(enumType, parameter.DefaultValue);
            }
            return parameter.DefaultValue;
        }
        if (parameter.ParameterType.IsValueType && Nullable.GetUnderlyingType(parameter.ParameterType) is null)
        {
            return Activator.CreateInstance(parameter.ParameterType);
        }
        return null;
    }

    private static object? GetArgument(this IServiceProvider argumentProvider, Type type, params object[] args)
    {
        if (type.IsInstanceOfType(argumentProvider))
        {
            return argumentProvider;
        }
        // 优先从 argumentProvider 中获取参数值
        var value = args?.FirstOrDefault(x => type.IsInstanceOfType(x))
                 ?? argumentProvider.GetService(type);
        if (value is not null)
        {
            return value;
        }

        return null;
    }


    /// <summary>
    /// 调用指定的委托
    /// </summary>
    /// <param name="provider"> 服务提供程序, 用于获取参数 </param>
    /// <param name="delegate"> 被调用的委托 </param>
    /// <param name="args"> 已确定的参数 </param>
    public static object Invoke(this IServiceProvider provider, Delegate @delegate, params object[]? args)
    {
        var arguments = provider.GetArguments(@delegate.Method.GetParameters(), args!);
        return @delegate.DynamicInvoke(arguments);
    }

    /// <summary>
    /// 调用指定的委托
    /// </summary>
    /// <param name="provider"> 服务提供程序, 用于获取参数 </param>
    /// <param name="delegate"> 被调用的委托 </param>
    /// <param name="args"> 已确定的参数 </param>
    public static Task InvokeAsync(this IServiceProvider provider, Delegate @delegate, params object[]? args)
        => provider.Invoke(@delegate, args) is Task task ? task : Task.CompletedTask;


    /// <summary>
    /// 调用指定的方法
    /// </summary>
    /// <param name="provider"> 服务提供程序, 用于获取参数 </param>
    /// <param name="method"> 被调用的方法 </param>
    /// <param name="args"> 已确定的参数 </param>
    public static object Invoke(this IServiceProvider provider, MethodInfo method, params object[]? args)
    {
        var arguments = provider.GetArguments(method.GetParameters(), args!);
        if (method.IsStatic)
        {
            return method.Invoke(null, arguments);
        }
        var target = provider.GetArgument(method.DeclaringType!, args!);
        return method.Invoke(target, arguments);
    }

    /// <summary>
    /// 调用指定的委托
    /// </summary>
    /// <param name="provider"> 服务提供程序, 用于获取参数 </param>
    /// <param name="method"> 被调用的方法 </param>
    /// <param name="args"> 已确定的参数 </param>
    public static Task InvokeAsync(this IServiceProvider provider, MethodInfo method, params object[]? args)
        => provider.Invoke(method, args) is Task task ? task : Task.CompletedTask;

}
